package com.zh.seckill.controller;


import cn.hutool.json.JSONUtil;
import com.ramostear.captcha.HappyCaptcha;
import com.ramostear.captcha.common.Fonts;
import com.ramostear.captcha.support.CaptchaStyle;
import com.ramostear.captcha.support.CaptchaType;
import com.zh.seckill.config.AccessLimit;
import com.zh.seckill.pojo.SeckillMessage;
import com.zh.seckill.pojo.SeckillOrder;
import com.zh.seckill.pojo.User;
import com.zh.seckill.rabbitmq.MQSenderMessage;
import com.zh.seckill.service.GoodsService;
import com.zh.seckill.service.OrderService;
import com.zh.seckill.service.SecKillOrderService;
import com.zh.seckill.vo.GoodsVo;
import com.zh.seckill.vo.RespBean;
import com.zh.seckill.vo.RespBeanEnum;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Controller
@RequestMapping("/seckill")
public class SecKillController implements InitializingBean {

    @Resource
    private OrderService orderService;
    @Resource
    private GoodsService goodsService;
    @Resource
    private SecKillOrderService secKillOrderService;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private MQSenderMessage mqSenderMessage;

    private HashMap<Long, Boolean> entryStockMap = new HashMap<>();

//    @RequestMapping("/{path}/doSeckill")
//    @ResponseBody
//    public RespBean doSeckill(@PathVariable String path, User user, Long goodsId) {
//
//
//        // System.out.println("---------------v1.0---------------------");
////
////        if (user == null) {
////            return "login";
////        }
////        model.addAttribute("user", user);
////
////        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
////        if (goodsVo.getStockCount() < 1) {
////            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
////            return "secKillFail";
////        }
////
////        SeckillOrder seckillOrder = secKillOrderService.getOne(new QueryWrapper<SeckillOrder>()
////                .eq("user_id", user.getId()).eq("goods_id", goodsId));
////        if (seckillOrder != null) {
////            model.addAttribute("errmsg", RespBeanEnum.REPEAT_ERROR.getMessage());
////            return "secKillFail";
////        }
////
////        Order order = orderService.seckill(user, goodsVo);
////        if (order == null){
////            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
////            return "secKillFail";
////        }
////
////        model.addAttribute("order", order);
////        model.addAttribute("goods", goodsVo);
//
//        //  System.out.println("---------------v2.0---------------------");
//
//
////        if (user == null) {
////            return "login";
////        }
////        model.addAttribute("user", user);
////
////        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
////        if (goodsVo.getStockCount() < 1) {
////            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
////            return "secKillFail";
////        }
////
////
////        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue()
////                .get("order:" + user.getId() + ":" + goodsVo.getId());
////        if (o != null) {
////            model.addAttribute("errmsg", RespBeanEnum.REPEAT_ERROR.getMessage());
////            return "secKillFail";
////        }
////
////
////        Order order = orderService.seckill(user, goodsVo);
////        if (order == null) {
////            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
////            return "secKillFail";
////        }
//
////        model.addAttribute("order", order);
////        model.addAttribute("goods", goodsVo);
//        // System.out.println("---------------v3.0---------------------");
//
////
////        if (user == null) {
////            return "login";
////        }
////        model.addAttribute("user", user);
////
////        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
////        if (goodsVo.getStockCount() < 1) {
////            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
////            return "secKillFail";
////        }
////
////        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue()
////                .get("order:" + user.getId() + ":" + goodsVo.getId());
////        if (o != null) {
////            model.addAttribute("errmsg", RespBeanEnum.REPEAT_ERROR.getMessage());
////            return "secKillFail";
////        }
////
////        if (entryStockMap.get(goodsId)) {
////            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
////            return "secKillFail";
////        }
////
////        //redis 预减库存
////        Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
////        if (decrement < 0) {
////
////            entryStockMap.put(goodsId, true);
////            redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
////            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
////            return "secKillFail";
////        }
////
////        //抢购
////        Order order = orderService.seckill(user, goodsVo);
////        if (order == null) {
////            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
////            return "secKillFail";
////        }
////        model.addAttribute("order", order);
////        model.addAttribute("goods", goodsVo);
//
//
//        //System.out.println("---------------v4.0---------------------");
//        //实现秒杀异步请求
//        if (user == null) {
//
//            return RespBean.error(RespBeanEnum.SESSION_ERROR);
//        }
//        if (!orderService.checkPath(user, goodsId, path)) {
//
//            return RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
//        }
//
//        // 判断库存
//        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);
//        if (goodsVo.getStockCount() < 1) {
//            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
//        }
//        // 判断是否重复抢购
//        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue()
//                .get("order:" + user.getId() + ":" + goodsVo.getId());
//        if (o != null) {
//
//            return RespBean.error(RespBeanEnum.REPEAT_ERROR);
//        }
//        // 判断是否有库存
//        if (entryStockMap.get(goodsId)) {
//
//            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
//        }
//
//        //redis 预减库存
//        Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
//        if (decrement < 0) {
//
//            entryStockMap.put(goodsId, true);
//            redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
//            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
//        }
//
//        //抢购
//        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
//        mqSenderMessage.sendMessage(JSONUtil.toJsonStr(seckillMessage));
//        return RespBean.error(RespBeanEnum.SEK_KILL_WAIT);
//    }

    @RequestMapping("/doSeckill")
    public String doSeckill(Model model, User user, Long goodsId) {


        if (user == null) {//用户没有登录
            return "login";
        }
        //将user放入到model, 下一个模板可以使用
        model.addAttribute("user", user);

        //获取到goodsVo
        GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);

        //判断库存
        if (goodsVo.getStockCount() < 1) {//没有库存
            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
            return "secKillFail";//错误页面
        }

        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue()
                .get("order:" + user.getId() + ":" + goodsVo.getId());
        if (null != o) { //说明该用户已经抢购了该商品
            model.addAttribute("errmsg", RespBeanEnum.REPEAT_ERROR.getMessage());
            return "secKillFail";//错误页面
        }

        if (entryStockMap.get(goodsId)) {
            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
            return "secKillFail";//错误页面
        }

        String uuid = UUID.randomUUID().toString();
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);
        if (lock) {
            Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
            if (decrement < 0) {
                entryStockMap.put(goodsId, true);
                redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
                redisTemplate.execute(redisScript, Arrays.asList("lock"), uuid);
                model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
                return "secKillFail";//错误页面
            }
            redisTemplate.execute(redisScript, Arrays.asList("lock"), uuid);

        } else {
            model.addAttribute("errmsg", RespBeanEnum.SEC_KILL_RETRY.getMessage());
            return "secKillFail";//错误页面
        }


        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
        mqSenderMessage.sendMessage(JSONUtil.toJsonStr(seckillMessage));
        model.addAttribute("errmsg", "排队中...");
        return "secKillFail";

    }

    @RequestMapping("/path")
    @ResponseBody
    @AccessLimit(second = 5, maxCount = 5, needLogin = true)
    public RespBean getPath(User user, Long goodsId,String captcha ,HttpServletRequest request) {

        if (user == null || goodsId < 0 || !StringUtils.hasText(captcha)) {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }

        //接口限流防刷
//        String uri = request.getRequestURI();
//        ValueOperations valueOperations = redisTemplate.opsForValue();
//        String key = uri + ":" + user.getId();
//        Integer count = (Integer) valueOperations.get(key);
//        if (count == null) {//说明还没有key,就初始化，值为1, 过期时间为5秒
//            valueOperations.set(key, 1, 5, TimeUnit.SECONDS);
//        } else if (count < 5) {//说明正常访问
//            valueOperations.increment(key);
//        } else {//说明用户在刷接口
//            return RespBean.error(RespBeanEnum.ACCESS_LIMIT_REACHED);
//        }



        if (!orderService.checkCaptcha(user, goodsId, captcha)) {
            return RespBean.error(RespBeanEnum.CAPTCHA_ERROR);
        }

        String path = orderService.createPath(user, goodsId);
        return RespBean.success(path);
    }

    //验证码
    //生成验证码-happyCaptcha
    @RequestMapping("/captcha")
    public void happyCaptcha(HttpServletRequest request, HttpServletResponse response, User user, Long goodsId) {
        //生成验证码，并输出
        //注意，该验证码，默认就保存到session中, key是 happy-captcha
        HappyCaptcha.require(request, response)
                .style(CaptchaStyle.ANIM)               //设置展现样式为动画
                .type(CaptchaType.NUMBER)               //设置验证码内容为数字
                .length(6)                              //设置字符长度为6
                .width(220)                             //设置动画宽度为220
                .height(80)                             //设置动画高度为80
                .font(Fonts.getInstance().zhFont())     //设置汉字的字体
                .build().finish();                      //生成并输出验证码

        //把验证码的值保存Redis 设置了验证码的失效时间100s
        //key: captcha:userId:goodsId
        redisTemplate.opsForValue().set("captcha:" + user.getId() + ":" + goodsId
                , (String) request.getSession().getAttribute("happy-captcha"), 100, TimeUnit.SECONDS);
    }

    @Override
    public void afterPropertiesSet() throws Exception {

        if (CollectionUtils.isEmpty(goodsService.findGoodsVo())) {
            return;
        }
        goodsService.findGoodsVo().forEach(goodsVo -> {
                    redisTemplate.opsForValue().set("seckillGoods:" + goodsVo.getId(), goodsVo.getStockCount());
                    entryStockMap.put(goodsVo.getId(), false);
                }
        );
    }


}
